//============================================================================
// Name        : Parser.cpp
// Author      : Casey Kelso
// Copyright   : Kineticsproject.com (c) 2010
// Description : Parser for ad2usb incoming data
// Created     : September 14th 2010
//============================================================================
#define DEBUG 0

#include "Parser.h"
#include <string>
#include <stdlib.h>
#include <cstdio>
#include "trace.h"


using namespace std;

Parser::Parser() : ENDOFLINE('\n'), STATUSFIELDSTART('['), STATUSFIELDEND(']'), STATUSFIELDLENGTH(20), FIELDDELIMITER(','), ALPHAMESSAGEDELIMITER('"')
{

}

Parser::~Parser()
{

}

bool Parser::validateCharacter(const unsigned char input, const unsigned char* charList, const int size)
{
    bool result = false;
    return(result);
}

bool Parser::Process(const unsigned char* data, int size, list<AlarmStatus> &alarmStatusList)
{

    static eState state = STATE_INITIAL; //private to this function
    static std::string statusFieldString;
    static std::string alphaNumericMessageString;

    bool result = false;
    static AlarmStatus CurrentAlarmStatus;



    for (int i = 0; i < size; i++)
    {

        switch(state)
        {
            case	STATE_INITIAL:
            {
                // the messages have repeated delimiters, so we're throwing away the first message just to be safe
                if (ENDOFLINE == data[i])
                {
                    state = STATE_WAITING_FOR_MESSAGE;
                    TRACE("TRANSITION: waiting\r\n");
                }
                break;
            }

            case    STATE_WAITING_FOR_MESSAGE:
            {
                if (STATUSFIELDSTART == data[i])
                {
                    TRACE("TRANSITION: Collecting\r\n");
                    state = STATE_COLLECTING_STATUS_FIELD;
                }
                break;
            }

            case	STATE_COLLECTING_STATUS_FIELD:
            {

                if (statusFieldString.length() > STATUSFIELDLENGTH) // sanity check
                {
                    state = STATE_UNKNOWN; // we are LOST in the message!
                    statusFieldString.clear();
                    TRACE("TRANSITION: Unknown\r\n");
		   
                }
                else if (STATUSFIELDEND == data[i])
                {
                    state = STATE_STATUS_FIELD_END;
                    CurrentAlarmStatus.setAlarmStatusMessage(statusFieldString);
                    statusFieldString.clear();
                    TRACE("TRANSITION: End Of Status Field\r\n");
                }
                else
                {
                    char a = data[i];
                    statusFieldString.append(1,a);
                }

                break;
            }

	    case	STATE_STATUS_FIELD_END:
	    {
		if (FIELDDELIMITER == data[i])
		{
			state = STATE_COLLECTING_ZONE_FAULT;
			TRACE("TRANSITION: Collecting Zone Fault\r\n");
		}
		break;
	    }

        case        STATE_COLLECTING_ZONE_FAULT:
	    {
		if (FIELDDELIMITER == data[i])
		{
            state = STATE_ZONE_FAULT_END;
			TRACE("TRANSITION: Zone Fault End\r\n");
		}
		break;
	    }

	    case	STATE_ZONE_FAULT_END:
	    {
		state = STATE_IGNORE; // let's ignore the hexidecimal message and wait for the alphanumeric message
		TRACE("TRANSITION: State Ignore\r\n");
		break;
   	    }
		
	    case	STATE_IGNORE:
	    {
		if (ALPHAMESSAGEDELIMITER == data[i])
	        {
			state = STATE_COLLECTING_ALPHA_MESSAGE;
			TRACE("TRANSITION: State Collecting Alpha Message\r\n");
			alphaNumericMessageString.clear();
			
		}
		break;
	    }

	    case	STATE_COLLECTING_ALPHA_MESSAGE:
	    {
		if (ALPHAMESSAGEDELIMITER == data[i])
		{
			state = STATE_MESSAGE_END;
			TRACE("TRANSITION: State Message End\r\n");
		}
		else
		{
			alphaNumericMessageString.append(1,data[i]);
		}
		break;
	    }
	

        case	STATE_MESSAGE_END:
        {
//            if (CurrentAlarmStatus.isValid())
            {
                //emit AlarmStatus Object

                alarmStatusList.push_back(CurrentAlarmStatus);
                CurrentAlarmStatus.setValid(true); //reuse the same value, no dynamic allocation here :)
                TRACE("GOT IT!\r\n");
            }

            TRACE("Alpha Numeric String: %s\r\n",alphaNumericMessageString.c_str());
            TRACE("TRANSITION: Waiting for a message\r\n");
            state = STATE_WAITING_FOR_MESSAGE;

            break;
        }

        case	STATE_UNKNOWN:
        {
            // wait for end of message
            if (ENDOFLINE == data[i])
            {
                state = STATE_WAITING_FOR_MESSAGE;
            }
            break;
        }

        default:
        {
            break;
        }

        }
	}

	return(result);
}

/*

Notes from this URL:
http://www.nutech.com/index.php?option=com_fireboard&Itemid=74&func=view&catid=4&id=5


Firmware documentation V2

Output Format
All lines that start with ! are information
A line that starts with !> is a prompt for input
All other lines are real panel data

Each line ends with a CR LF also known as rn or chr(10),chr(13)

In a normal panel output message the data has 4 sections.

Section #1 the bit fields easy to parse interpretations of the
raw panel code. Each bit can be one of three states.
0 = Off 1 = On - = Unknown or float or in the case of BEEP
a single digit number.


Example: [100000010000--------]
Position #1 = READY
Position #2 = ARMED AWAY
Position #3 = ARMED HOME
Position #4 = BACK LIGHT
Position #5 = Programming Mode
Position #6 = Beep 1-7 ( 3 = beep 3 times )
Position #7 = A ZONE OR ZONES ARE BYPASSED
Position #8 = AC Power
Position #9 = CHIME MODE
Position #10 = ALARM WAS TRIGGERED (Sticky Bit)
Position #11 = ALARM BELL
Position #12 = BATTERY LOW
Position #13 - #20 unused

Section #2 Numeric indicator if message is zone 3 fault this will be 003

Section #3 Raw panel binary message data
Example: [f70700060010801c08020000000000]
This is the raw panel binary message data. Each
byte is represent as a 2 char hex bytes.

Byte 2-5 is the 32 bit keypad address mask in the above example the
mask is 07000600. This mask defines if the message should be listened
to based upon the devices address. The addresses for every bit in the
32 bit word are as follows from the MSB to LSB
07,06,05,04,03,02,01,00
15,14,13,12,11,10,09,08
23,22,21,20,19.18,17,16
31,30,29,28,27,26,25,24

If you have multiple partitions setup a keypad is assigned to a given
partition and may show "READY" where another keypad on the other
partition may show "ARMED" so be aware if the issues of being able to
see all messages from the alarm panel and only use the ones are intend
to listen to. You may also want to ignore System messages that have no
address mask bits set and are intended for common keypads or monitoring
multiple zones.



Section #4 Alpha Keypad Message
Example: "****DISARMED**** ZONES FAULTED "
This is in quotes and is the message that is displayed on an alpha keypad.

Here is a full message from a vista 50p


[01000001000---------],0e5,[f707000600e5800c0c020000],"ARMED ***AWAY***** ALL SECURE **"
[001000010000--------],010,[f70700060010808c08020000],"ARMED ***STAY** ZONE BYPASSED "


The alarm panel does not sent out CLEARED messages when a zone is restored.
To determine if a zone is clear you have to watch all faulted zones and if one
stops reporting as faulted consider it restored. So you have to track the state
of the zone messages. To get a list of faulted zones send a * to the panel when
you see a message that contains "Hit * for faults". You will then get a list of
faulted zones. Each zone is sent out in numeric order from lowest to highest.
If a zone faults in the middle of sending out a faulted zone list it will display
the latest fault then continue sending out faults that are for zones that are
larger then the newest fault.


Keyboard Commands
= reboot
@ Diagnostic bit setting #2
! Configure device
L Virtual Zone expander message input

To send keypad characters to the panel the following
characters are valid.
0123456789*#
F1 = chr(1)
F2 = chr(2)
F3 = chr(3)
F4 = chr(4)



#2 Diagnostic bits must be entered as base 10 values
16 Raw RX Data dump will dump raw data as seen by
the software uart to the serial port on the DO line of the panel.
32 Raw TX Data dump will dump raw data as seen by
the software uart to the serial port on the DI line of the panel.


Diagnostic LED
A heartbeat for the USB command and control thread. Not active in boot loader.

!EXP Messages

If you have one or more zone expanders connected to the alarm panel you will be able to see raw messages from the zone expander as zones are faulted.

The EXP messages has the following format
It starts with the following "!EXP:" with no quotes the
next section is the zone expander address followed by a comma then the channel on the expander 1-8 followed by a comma and finally the raw data for that channel.

Example of zone expander #7 input #1 faulting and then restored.
!EXP:07,01,01
!EXP:07,01,00


!RFX Messages

If you have a RF receiver attached to your alarm you will see messages from
any 5800 RF device in the range of the receiver regardless if the alarm
panel was configured to listen to the 5800 device.

When the alarm is armed stay you wont see any "FAULT" messages for interior
zones so if you have motion sensors in your building and you arm stay you
loose visibility of these faults. One option is to put the interior sensors
on a separate partition so you still see these zones and the outside sensors
on a separate zone. This would allow you to see faults inside of the building
while the outside is armed. Another option is to listen directly to the RF
sensors messages and use these to control your home automation needs.

The RFX messages has the following format
It starts with the following "!RFX:" with no quotes the
next section is the 7 digit Serial Number of the RF device followed by a comma
and the 8 bit hex message from the device.

This is a 5800Micra sensor faulting
!RFX:0180036,80

This is a 5800Micra sensor restoring
!RFX:0180036,00

This is a 5804 keyfob pressing the loop 2 button and
it has a low battery.
!RFX:0307854,22


Each bit of the 8 bit message has a meaning as follows.

bit Meaning
============
1 UNKNOWN at this time
2 battery
3 supervision
4 UNKNOWN at this time
5 loop 3
6 loop 2
7 loop 4
8 loop 1

Refer to the "5800 SERIES TRANSMITTER INPUT LOOP IDENTIFICATION"
section of your alarms programming manual for what loop each device sends.



Zone Expander Emulator
If you have a zone expander emulation enabled you can send faults and clear for
a zone by sending an L followed by the two digit zone number padded with zeros
followed by a 0 1 or 2

So to fault zone 09 and you are emulating the first zone expander
you would send L091 to open the zone and to clear that zone
you would send L090. To fault a zone and simulate a wire problem
short etc you would send L092

*/
